import { PL00ParserVisitor } from "../antlr/PL00ParserVisitor";
import { PL00SymbolTable } from "./PL00SymbolTable";
import {
  Create,
  ProcedureSymbol,
  FunctionSymbol,
  FunctionArgument,
  IntegerSymbol,
  StringSymbol,
  RealSymbol,
} from "./PL00Symbols";
import BuiltinsFactory from "./PL00BuiltInFunctions";
import { PL00SymbolCalculator } from "./PL00SymbolCalculator";
import { importModuleFactory } from "./PL00Import";
import { create } from "domain";

/**
 * 解释访问器类的工厂函数
 * @param output 输出函数
 * @param input 输入函数
 * @param modules 模块
 */
function PL00InterpretVistorFactory(
  output: Function,
  input: Function,
  modules: { [moduleName: string]: { [functionName: string]: FunctionSymbol } }
) {
  // 符号表
  const symbolTable = new PL00SymbolTable();
  symbolTable.push();
  // 内置函数
  const builtins = BuiltinsFactory(output, input);
  // 导入内置函数
  for (const funcName in builtins) {
    for (const func of builtins[funcName]) {
      symbolTable.add(funcName, func);
    }
  }
  // 这里采用原型链继承方式
  function PL00InterpretVistor() {
    PL00ParserVisitor.call(this);
    this.symbolTable = symbolTable;
    // 是否正常结束
    this.completed = true;
    // 循环/调用限制
    this.loopRestriction = 1000;
    return this;
  }
  PL00InterpretVistor.prototype = Object.create(PL00ParserVisitor.prototype);
  PL00InterpretVistor.prototype.constructor = PL00InterpretVistor;

  // Visit a parse tree produced by PL00Parser#program.
  PL00InterpretVistor.prototype.visitProgram = function (ctx) {
    this.visitChildren(ctx);
  };

  // Visit a parse tree produced by PL00Parser#importstmt.
  PL00InterpretVistor.prototype.visitImportstmt = function (ctx) {
    // 导入模块的函数
    const importModule = importModuleFactory(this);
    for (let i = 0; 2 * i + 3 <= ctx.children.length; i++) {
      let moduleName = this.visitPath(ctx.children[i * 2 + 1]);
      // 如果已经记录过导入的模块，那么只需要复制到符号表即可
      if (moduleName in modules) {
        for (const functionName in modules[moduleName]) {
          symbolTable.add(functionName, modules[moduleName][functionName]);
        }
      } else {
        symbolTable.importModule(moduleName);
        if (!(moduleName in modules)) {
          modules[moduleName] = {};
        }
        importModule(moduleName).then((functions) => {
          for (const func of functions) {
            modules[moduleName][func.name] = func.value;
          }
        });
      }
    }
  };

  // Visit a parse tree produced by PL00Parser#path.
  PL00ParserVisitor.prototype.visitPath = function (ctx): string {
    return ctx.children.map((x) => x.symbol.text).join("");
  };

  // Visit a parse tree produced by PL00Parser#block.
  PL00InterpretVistor.prototype.visitBlock = function (ctx) {
    const children = this.visitChildren(ctx);
    const result = children[children.length - 1];
    return result;
  };

  // Visit a parse tree produced by PL00Parser#consts.
  PL00InterpretVistor.prototype.visitConsts = function (ctx) {
    const children = this.visitChildren(ctx);
    // 实际上这里的常量完全是按照变量来存储的，只是多了一个赋值环节，你甚至可以改变常量
    for (let i = 0; 4 * i + 5 <= children.length; i++) {
      const identName = children[4 * i + 1];
      const value = children[4 * i + 3];
      symbolTable.add(identName, value);
    }
  };

  // Visit a parse tree produced by PL00Parser#vars.
  PL00InterpretVistor.prototype.visitVars = function (ctx) {
    const children = this.visitChildren(ctx);
    let position = 1;
    let temp = [];
    // 统一处理相同类型的声明
    while (true) {
      if (position === ctx.children.length) {
        break;
      }
      const element = ctx.children[position];
      if ("symbol" in element) {
        if (element.symbol.text == ",") {
          position++;
        } else if (element.symbol.text == ":") {
          const typeName = children[position + 1];
          for (const name of temp) {
            symbolTable.add(name, Create(typeName));
          }
          position += 3;
          temp = [];
          continue;
        }
      }
      // console.log(position)
      // console.log(children);
      temp.push(children[position++]);
    }
  };

  // Visit a parse tree produced by PL00Parser#procedure.
  PL00InterpretVistor.prototype.visitProcedure = function (ctx) {
    // 说起来，过程完全没有测试过呢
    const procedureName = this.visitIdent(ctx.children[1]);
    symbolTable.add(procedureName, new ProcedureSymbol(ctx.children[3]));
  };

  // Visit a parse tree produced by PL00Parser#functionDef.
  PL00InterpretVistor.prototype.visitFunctionDef = function (ctx) {
    // 获取函数名
    const functionName = ctx.children[1].symbol.text;
    // 获取函数体和返回类型
    const functionSymbol = new FunctionSymbol(
      ctx.children[ctx.children.length - 2],
      this.visitType(ctx.children[ctx.children.length - 4])
    );
    // 获取函数参数
    for (let i = 0; i * 4 + 12 <= ctx.children.length; i++) {
      const identName = this.visitIdent(ctx.children[i * 4 + 3]);
      const typeName = this.visitIdent(ctx.children[i * 4 + 5]);
      functionSymbol.params.push(new FunctionArgument(identName, typeName));
    }
    // 添加到符号表
    symbolTable.add(functionName, functionSymbol);
    return { name: functionName, value: functionSymbol };
  };

  // Visit a parse tree produced by PL00Parser#functionDefs.
  PL00InterpretVistor.prototype.visitFunctionDefs = function (ctx) {
    return this.visitChildren(ctx);
  };

  // Visit a parse tree produced by PL00Parser#statement.
  PL00InterpretVistor.prototype.visitStatement = function (ctx) {
    if (ctx.children === null) {
      return null;
    }
    for (const child of ctx.children) {
      const result = child.accept(this);
      // 处理return/continue/break语句
      if (
        result &&
        ("return" in result || "continue" in result || "break" in result)
      ) {
        return result;
      }
    }
  };

  // Visit a parse tree produced by PL00Parser#assignstmt.
  PL00InterpretVistor.prototype.visitAssignstmt = function (ctx) {
    let children = this.visitChildren(ctx);
    let name = children[0];
    if (children[2] !== undefined) {
      symbolTable.bindValue(name, children[2].value);
    }
  };

  // Visit a parse tree produced by PL00Parser#vectorassignstmt.
  PL00InterpretVistor.prototype.visitVectorassignstmt = function (ctx) {
    let children = this.visitChildren(ctx);
    let name = children[0];
    let array = symbolTable.get(name);
    // 分为字符串赋值和数组赋值两种情况
    if (array.type === "String") {
      const position = children[2].value;
      const stringSymbol = array as StringSymbol;
      stringSymbol.value =
        stringSymbol.value.substr(0, position) +
        children[5].value +
        stringSymbol.value.substr(position + 1);
    } else {
      array.value[children[2].value] = Create(children[5].type);
      array.value[children[2].value].value = children[5].value;
    }
  };

  // Visit a parse tree produced by PL00Parser#callstmt.
  PL00InterpretVistor.prototype.visitCallstmt = function (ctx) {
    // 调用过程
    const procedureName = this.visitIdent(ctx.children[1]);
    symbolTable.push();
    this.visitBlock(symbolTable.get(procedureName).value);
    symbolTable.pop();
  };

  // Visit a parse tree produced by PL00Parser#returnstmt.
  PL00InterpretVistor.prototype.visitReturnstmt = function (ctx) {
    // 并没有考虑在非函数体中写此语句的后果
    return { return: this.visitExpression(ctx.children[1]) };
  };

  // Visit a parse tree produced by PL00Parser#beginstmt.
  PL00InterpretVistor.prototype.visitBeginstmt = function (ctx) {
    // console.log(ctx)
    if (this.completed === false) {
      return null;
    }
    for (let i = 0; i * 2 + 3 <= ctx.children.length; ++i) {
      const temp = this.visitStatement(ctx.children[i * 2 + 1]);
      // console.log(temp)
      if (temp && ("return" in temp || "continue" in temp || "break" in temp)) {
        return temp;
      }
    }
  };

  // Visit a parse tree produced by PL00Parser#ifstmt.
  PL00InterpretVistor.prototype.visitIfstmt = function (ctx) {
    if (this.visitCondition(ctx.children[1])) {
      const result = this.visitStatement(ctx.children[3]);
      // console.log(result)
      return result;
    } else {
      // 这里是有else语句的情况
      if (ctx.children.length == 6) {
        const result = this.visitStatement(ctx.children[5]);
        return result;
      }
    }
    // return this.visitChildren(ctx);
  };

  // Visit a parse tree produced by PL00Parser#whilestmt.
  PL00InterpretVistor.prototype.visitWhilestmt = function (ctx) {
    while (true) {
      this.loopRestriction--;
      // 限制循环次数
      if (0 > this.loopRestriction) {
        this.completed = false;
        throw new Error("超出循环限制");
      }
      // 每次循环都重新遍历循环体的语法树
      const condition = this.visitCondition(ctx.children[1]);
      if (condition == false) {
        break;
      }
      // 处理break、continue、return
      const result = this.visitStatement(ctx.children[3]);
      if (result) {
        if ("break" in result) {
          break;
        }
        if ("continue" in result) {
          continue;
        }
        if ("return" in result) {
          return result.return;
        }
      }
    }
  };

  // Visit a parse tree produced by PL00Parser#breakstmt.
  PL00InterpretVistor.prototype.visitBreakstmt = function (ctx) {
    return { break: true };
  };

  // Visit a parse tree produced by PL00Parser#continuestmt.
  PL00InterpretVistor.prototype.visitContinuestmt = function (ctx) {
    return { continue: true };
  };

  // Visit a parse tree produced by PL00Parser#condition.
  PL00InterpretVistor.prototype.visitCondition = function (ctx) {
    var children = this.visitChildren(ctx);
    const op = ctx.children[1].symbol.text;
    const calculator = new PL00SymbolCalculator(this, symbolTable);
    // 采用减法实现比较
    const temp = calculator.Sub(children[0], children[2]);
    switch (op) {
      case "=":
        return temp.value === 0;
      case "#":
        return temp.value !== 0;
      case "<":
        return temp.value < 0;
      case "<=":
        return temp.value <= 0;
      case ">":
        return temp.value > 0;
      case ">=":
        return temp.value >= 0;
      default:
        return false;
    }
  };

  // Visit a parse tree produced by PL00Parser#callfunction.
  PL00InterpretVistor.prototype.visitCallfunction = function (
    ctx,
    firstParam = null
  ) {
    // 函数名
    const funcName = ctx.children[0].symbol.text;
    // 参数
    const params = [];
    if (firstParam !== null) {
      params.push(firstParam);
    }
    for (let i = 0; 2 * i + 4 <= ctx.children.length; i++) {
      const result = this.visitExpression(ctx.children[i * 2 + 2]);
      params.push(result);
    }
    // console.log(funcName)
    // console.log(params)
    return this.callFunction(funcName, params);
  };

  PL00InterpretVistor.prototype.callFunction = function (funcName, params) {
    // console.log(funcName);
    // console.log(this.loopRestriction)
    // 超出限制直接抛出异常
    this.loopRestriction--;
    if (0 > this.loopRestriction) {
      this.completed = false;
      const functionSymbol = symbolTable.getFunction(funcName, params);
      throw new Error(
        "超出调用限制，在'" +
          funcName +
          "(" +
          functionSymbol.params.map((x) => x.type).join(",") +
          ")'"
      );
    }
    // console.log(funcName);
    // console.log(params)
    // 获取匹配函数
    const functionSymbol = symbolTable.getFunction(funcName, params);
    // console.log(functionSymbol);
    if (functionSymbol === null) {
      throw new Error(funcName + " not found");
    }
    // 函数体可能是js实现的也可能是语法树的子树
    if (typeof functionSymbol.value === "function") {
      const result = functionSymbol.value(...params);
      // console.log(result);
      return result;
    }
    // 作用域
    symbolTable.push();
    for (let i = 0; i < params.length; ++i) {
      symbolTable.add(functionSymbol.params[i].name, params[i]);
    }
    const result = this.visitBlock(functionSymbol.value);
    symbolTable.pop();
    // console.log("return block")
    // console.log(result);
    if ("return" in result) {
      return result.return;
    }
    return result;
  };

  // Visit a parse tree produced by PL00Parser#expression.
  PL00InterpretVistor.prototype.visitExpression = function (ctx) {
    let children = this.visitChildren(ctx);
    let offset = 0;
    if (children.length === 1) {
      return children[0];
    }
    let result = new IntegerSymbol(0);
    // 当表达式以加号或减号开头时，不会进入此分支
    if (children[0] !== undefined) {
      offset = 1;
      result = children[0];
    }
    const calculator = new PL00SymbolCalculator(this, symbolTable);
    for (let i = 0; i * 2 + offset < children.length; ++i) {
      if (ctx.children[i * 2 + offset].symbol.text == "+") {
        result = calculator.Add(result, children[i * 2 + offset + 1]);
      } else {
        result = calculator.Sub(result, children[i * 2 + offset + 1]);
      }
    }
    return result;
  };

  // Visit a parse tree produced by PL00Parser#term.
  PL00InterpretVistor.prototype.visitTerm = function (ctx) {
    let children = this.visitChildren(ctx);
    let result = children[0];
    const calculator = new PL00SymbolCalculator(this, symbolTable);
    for (let i = 0; i * 2 + 1 < children.length; i++) {
      if (ctx.children[i * 2 + 1].symbol.text == "*") {
        result = calculator.Multiply(result, children[i * 2 + 2]);
      } else {
        result = calculator.Divide(result, children[i * 2 + 2]);
      }
    }
    return result;
  };

  // Visit a parse tree produced by PL00Parser#IntegerFactor.
  PL00InterpretVistor.prototype.visitIntegerFactor = function (ctx) {
    return this.visitChildren(ctx)[0];
  };

  // Visit a parse tree produced by PL00Parser#BracketExpr.
  PL00InterpretVistor.prototype.visitBracketExpr = function (ctx) {
    return this.visitExpression(ctx.children[1]);
  };

  // Visit a parse tree produced by PL00Parser#Index.
  PL00InterpretVistor.prototype.visitIndex = function (ctx) {
    const children = this.visitChildren(ctx);
    const factor = children[0];
    // console.log(ctx)
    if (factor.type === "String") {
      return new StringSymbol(factor.value[children[2].value]);
    } else {
      return factor.value[children[2].value];
    }
  };

  // Visit a parse tree produced by PL00Parser#StringFactor.
  PL00InterpretVistor.prototype.visitStringFactor = function (ctx) {
    return this.visitChildren(ctx)[0];
  };

  // Visit a parse tree produced by PL00Parser#FunctionCall.
  PL00InterpretVistor.prototype.visitFunctionCall = function (ctx) {
    return this.visitChildren(ctx)[0];
  };

  // Visit a parse tree produced by PL00Parser#IdentFactor.
  PL00InterpretVistor.prototype.visitIdentFactor = function (ctx) {
    const identName = this.visitChildren(ctx)[0];
    const result = symbolTable.get(identName);
    return result;
  };

  // Visit a parse tree produced by PL00Parser#RealFactor.
  PL00InterpretVistor.prototype.visitRealFactor = function (ctx) {
    return this.visitChildren(ctx)[0];
  };

  // Visit a parse tree produced by PL00Parser#FunctionChain.
  PL00InterpretVistor.prototype.visitFunctionChain = function (ctx) {
    const factor = ctx.children[0].accept(this);
    // 函数链可以利用正常调用函数的方法
    const result = this.visitCallfunction(ctx.children[2], factor);
    return result;
  };

  // Visit a parse tree produced by PL00Parser#type.
  PL00InterpretVistor.prototype.visitType = function (ctx) {
    const typeName = ctx.children[0].symbol.text;
    return typeName;
  };

  // Visit a parse tree produced by PL00Parser#ident.
  PL00InterpretVistor.prototype.visitIdent = function (ctx) {
    const identName = ctx.children[0].symbol.text;
    return identName;
  };

  // Visit a parse tree produced by PL00Parser#integer.
  PL00InterpretVistor.prototype.visitInteger = function (ctx) {
    const text = ctx.children[0].symbol.text;
    return new IntegerSymbol(parseInt(text));
  };

  // Visit a parse tree produced by PL00Parser#real.
  PL00InterpretVistor.prototype.visitReal = function (ctx) {
    const text1 = ctx.children[0].symbol.text;
    const text2 = ctx.children[2].symbol.text;
    return new RealSymbol(parseFloat(text1 + "." + text2));
  };

  // Visit a parse tree produced by PL00Parser#string.
  PL00InterpretVistor.prototype.visitString = function (ctx) {
    let text = ctx.children[0].symbol.text;
    text = text.substr(1, text.length - 2);
    return new StringSymbol(text);
  };

  return PL00InterpretVistor;
}

export { PL00InterpretVistorFactory };
